package com.gonsin.gview.logic.services;

import com.gonsin.gview.annotations.GContext;
import com.gonsin.gview.annotations.actions.GAction;
import com.gonsin.gview.annotations.data.*;
import com.gonsin.gview.annotations.dynamic.GDynamicView;
import com.gonsin.gview.annotations.model.ViewRequest;
import com.gonsin.gview.annotations.service.GViewApplication;
import com.gonsin.gview.annotations.GView;
import com.gonsin.gview.annotations.GViews;
import com.gonsin.gview.logic.GViewEnvironment;
import com.gonsin.gview.logic.config.GViewDynamicViewService;
import com.gonsin.gview.logic.exceptions.*;
import com.gonsin.gview.logic.model.ActionPo;
import com.gonsin.gview.logic.model.ClassView;
import com.gonsin.gview.logic.model.DataSourcePo;
import com.gonsin.gview.logic.parser.DynamicModelHolder;
import com.gonsin.gview.logic.parser.StanderModelHolder;
import com.gonsin.gview.logic.parser.StanderParser;
import com.gonsin.gview.logic.utils.GViewUtils;
import com.gonsin.gview.logic.utils.SpringUtil;
import com.gonsin.gview.model.*;
import com.gonsin.gview.model.dynamic.DynamicView;
import com.gonsin.gview.model.menu.GViewMenu;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Order
@Service
@Slf4j
public class GViewCoreServiceImpl {

    private Map<String, IViewModelHolder> holderMap;

    /**
     * 准备阶段的board列表，用于存放动态生成的view，在初始化最后阶段，添加到boardList中
     */
    private List<GViewModel> prepareBoardList;

    @Autowired private GViewDynamicViewService dynamicView;
//    @Autowired private GViewFutureBindingService bindingService;

    @Autowired(required = false) private IViewPermissionHandler permissionHandler;

    private List<IViewModelHolder> menuList;

    /**
     * key = viewName
     * value = 按钮
     * 表示按钮与跳转页面的关系，用于校验参数是否正确
     */
    private Map<String, List<GButtonModel>> buttonTargetView = new HashMap<>();

    private Set<String> viewNameList;

    /**
     * 已初始化的gview类
     */
    private Set<Class> gviewClassSet;
    private Map<String, ActionPo> actionModelMap = new HashMap<>();
    private Map<String, DataSourcePo> dataSourceMap = new HashMap<>();



    @Autowired private GViewDataServiceImpl dataService;
    @Autowired private GViewActionServiceImpl actionService;
    @Autowired private GViewGlobalEnumsServiceImpl globalEnumsService;
    @Autowired private StanderParser parser;


    /**
     * 根据执行名字查询GAction方法
     * @param action @GAction标注的value值
     * @return
     */
    public ActionPo findActionByName(String action){
        return actionModelMap.get(action);
    }

    /**
     * 根据名字查找数据源
     * @param dataSource @GDataSource标注的value值
     * @return
     */
    public DataSourcePo findDataSourceByName(String dataSource){
        return dataSourceMap.get(dataSource);
    }

    /**
     * 查询页面模板信息
     */
    public IViewModelHolder findViewByName(String viewName) {
        return holderMap.get(viewName);
    }

    /**
     * 查询页面模板信息
     */
    public GViewModel findModelByRequest(ViewRequest viewRequest) {
        IViewModelHolder viewModel = holderMap.get(viewRequest.getViewName());
        return viewModel.getViewModel(viewRequest, permissionHandler);
    }

    public Set<String> findAllBoard() {
        return viewNameList;
    }

    @EventListener(ApplicationReadyEvent.class)
    public void onApplicationStarting(ApplicationReadyEvent event) {

        holderMap = new ConcurrentHashMap<>();
        viewNameList = new HashSet<>();
        gviewClassSet = new HashSet<>();

        log.info("初始化GView");
        long now = System.currentTimeMillis();

        // 扫描所有的@GAction，查找所有的 点击事件
        SpringUtil.scannerFunctions(GViewApplication.getScanPackage(), new TypeFilter[]{
                // 执行方法
                new AnnotationTypeFilter(GAction.class),
                // 数据源
                new AnnotationTypeFilter(GDataSource.class),
        }, (clazz, method) -> {

            // 查找系统中所有的GAction 标注的方法
            GAction action = method.getDeclaredAnnotation(GAction.class);
            if(action != null){
                String actionId = action.value();
                actionId = actionId.equals("") ? method.getName() : actionId;
                if(actionModelMap.containsKey(actionId)){
                    ActionPo origin = actionModelMap.get(actionId);
                    if (origin.getActionService().equals(clazz) && origin.getActionMethod().equals(method)) {
                        if(GViewEnvironment.LOG){
                            log.warn("重复注入{}.{}", clazz.getName(), method.getName());
                        }
                        return;
                    }
                    throw new BaseGViewException(String.format("定义了2个相同的GAction方法，请查看%s的%s，和%s的%s",
                            origin.getActionService().getName(),
                            origin.getActionMethod().getName(),
                            clazz.getName(),
                            method.getName()));
                }
                if(GViewEnvironment.LOG){
                    log.warn("注入GAction {}.{}", clazz.getName(), method.getName());
                }
                actionModelMap.put(actionId, new ActionPo(clazz, method));
            }

            // 查找系统中所有的 GDataSource 标注的方法
            GDataSource dataSource = method.getDeclaredAnnotation(GDataSource.class);
            if(dataSource != null){
                String sourceName = dataSource.value();
                sourceName = sourceName.equals("") ? method.getName() : sourceName;
                if(dataSourceMap.containsKey(sourceName)){
                    DataSourcePo origin = dataSourceMap.get(sourceName);
                    if (origin.getSourceService().equals(clazz) && origin.getSourceMethod().equals(method)) {
                        if(GViewEnvironment.LOG){
                            log.warn("重复注入{}.{}", clazz.getName(), method.getName());
                        }
                        return;
                    }
                    throw new BaseGViewException(String.format("定义了2个相同的GDataSource方法，请查看%s的%s，和%s的%s",
                            origin.getSourceService().getName(),
                            origin.getSourceMethod().getName(),
                            clazz.getName(),
                            method.getName()));
                }
                if(GViewEnvironment.LOG){
                    log.warn("注入GDataChange {}.{}", clazz.getName(), method.getName());
                }
                dataSourceMap.put(sourceName, new DataSourcePo(clazz, method));
                return;
            }
        });


        // 扫描所有的@GBoard，查找用到的所有页面
//        SpringUtil.scannerPackage(GViewApplication.getScanPackage(), new TypeFilter[]{
//                new AnnotationTypeFilter(GView.class),
//                new AnnotationTypeFilter(GViews.class),
//        }, clazz -> {
//            if(GViewEnvironment.LOG){
//                log.info("检测到@GView : {}", clazz.getName());
//            }
//            List<String> boardNames = initBoardName(clazz);
//            boardNameList.addAll(boardNames);
//        });


        // 从静态的@GView标签中获取 GView 页面
        if(GViewEnvironment.LOG){
            log.info("【GView】开始扫描{}", GViewApplication.getScanPackage());
        }
        SpringUtil.scannerPackage(GViewApplication.getScanPackage(), new TypeFilter[]{
                new AnnotationTypeFilter(GView.class),
                new AnnotationTypeFilter(GViews.class),
        }, clazz -> {

            try {
                if(isInited(clazz)){
                    return;
                }
                List<StanderModelHolder> holders = initView(clazz);
                holders.forEach(m -> {
                    if(GViewEnvironment.LOG){
                        log.info("【GView】注入viewName = {}, class = {}", m.getViewName(), clazz.getName());
                    }
                    ViewRequest viewRequest = new ViewRequest();
                    viewRequest.setViewName(m.getViewName());
                    viewRequest.setInitialing(true);

                    m.getViewModel(viewRequest, permissionHandler);
                    holderMap.put(m.getViewName(), m);
                    viewNameList.add(m.getViewName());
                });
//                gviewClassSet.add(clazz);
            } catch (BaseGViewException ex) {
                if(GViewEnvironment.THROW_EXCEPTION_WHEN_INIT){
                    throw ex;
                } else {
                    log.error(ex.toString(), ex);
                }
            }
        });

        // 从静态的@GView标签中获取 GView 页面
        SpringUtil.scannerPackage(GViewApplication.getScanPackage(), new TypeFilter[]{
                new AnnotationTypeFilter(GDynamicView.class),
        }, clazz -> {

            try {
                if(isInited(clazz)){
                    return;
                }

                if(!DynamicView.class.isAssignableFrom(clazz)){
                    throw new BaseGViewException("{}必须继承 DynamicFormView、DynamicTableView、DynamicTabItemLink、 DynamicTabItemName、DynamicTabView 其中之一", clazz.getName());
                }

                Object instance = SpringUtil.getBean(clazz);
                if(instance == null){
                    try {
                        instance = clazz.newInstance();
                    } catch (Exception e) {
                        log.error("无法找到{}的spring bean类，而且无法实例化该类", clazz.getName());
                        throw new RuntimeException(e);
                    }
                }
                GDynamicView annotation = clazz.getAnnotation(GDynamicView.class);
                dynamicView.addDynamicView(annotation.viewName(), (DynamicView) instance);

            } catch (BaseGViewException ex) {
                if(GViewEnvironment.THROW_EXCEPTION_WHEN_INIT){
                    throw ex;
                } else {
                    log.error(ex.toString(), ex);
                }
            }
        });

        // 从动态的GView页面中获取(viewName重复的，以动态优先)
        List<DynamicModelHolder> views = dynamicView.getDynamicViews();
        views.forEach(m -> {
//            viewMap.put(m.getViewName(), m);
//            viewList.add(m);
            viewNameList.add(m.getViewName());
            holderMap.put(m.getViewName(), m);
        });


//        bindingService.runAfterView();

        // 检测所有view中，跳转viewName是否有效，actionName是否有效，dataSource是否有效
//        ViewValidate validate = new ViewValidate();
//        for(GViewModel view : viewList){
//            ValidateContext validateContext = new ValidateContext(
//                    view.getViewName(),
//                    this,
//                    actionService,
//                    dataService);
//            validate.validate(validateContext, view);
//        }

        log.info("初始化GView完成，耗时" + new SimpleDateFormat("ss.SSS").format(System.currentTimeMillis() - now) + "秒");
    }

    private List<StanderModelHolder> initView(Class<?> clazz) {
        List<StanderModelHolder> result = new ArrayList<>();

        GView gview = clazz.getAnnotation(GView.class);

        if(gview == null){
            GViews gviews = clazz.getAnnotation(GViews.class);
            for(GView view : gviews.value()){
//                result.add(initBoard(view, clazz));
                result.add(new StanderModelHolder(view, clazz, parser));
            }
        } else {
//            result.add(initBoard(gview, clazz));
            result.add(new StanderModelHolder(gview, clazz, parser));
        }
        return result;
    }

    /**
     * 是否已初始化某个@GView class
     * @param gviewClass
     * @return
     */
    private boolean isInited(Class<?> gviewClass) {
        if(gviewClassSet.contains(gviewClass)){
            return true;
        }
        gviewClassSet.add(gviewClass);
        return false;
    }

    /**
     * 返回可以显示在菜单栏上的view
     */
    public List<IViewModelHolder> findMenuViews() {
        if(menuList == null){
            Collection<IViewModelHolder> list = holderMap.values();
            List<IViewModelHolder> menus = list.stream()
                    .filter(m -> {
                        return CollectionUtils.isEmpty(m.getProps());
                    })
                    .sorted(Comparator.comparing(IViewModelHolder::getViewName))
                    .collect(Collectors.toList());
            menuList = menus;
        }
        return menuList;
    }


    /**
     * 将按钮添加到map中，绑定按钮和view的关系，用于校验参数是否正确
     */
    public void putViewButton(String viewName, GButtonModel button){
        List<GButtonModel> buttons = buttonTargetView.computeIfAbsent(viewName, v -> {
            return new ArrayList<>();
        });
        buttons.add(button);
    }


    /**
     * 返回按钮和跳转的view的关系
     */
    public List<GButtonModel> findButtonByView(String viewName) {
        return buttonTargetView.get(viewName);
    }


    public List<GViewMenu> getAllMenus(HttpServletRequest request, HttpServletResponse response) {

        ViewRequest r = new ViewRequest();
        r.setRequest(request);
        r.setResponse(response);
        r.setViewName("menu");
        r.setProps(new HashMap<>());
        return holderMap.values().stream().filter(v -> {
            return CollectionUtils.isEmpty(v.getProps());
        }).map(v -> {
            GViewModel model = v.getViewModel(r, permissionHandler);
            GViewMenu menus = new GViewMenu(v.getViewName(), model.getTitle(), null, GViewUtils.gview(v.getViewName()), null);
            return menus;
        }).collect(Collectors.toList());
    }
}
